#ifndef REWINE_REWINE_H
#define REWINE_REWINE_H

/* calling convention */

#if defined(_MSC_VER)
   // all is done
#elif defined(__GNUC__)
#  if defined(__i386__)
#    if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2)) || defined(__APPLE__)
#      define __stdcall __attribute__((__stdcall__)) __attribute__((__force_align_arg_pointer__))
#      define __cdecl __attribute__((__cdecl__)) __attribute__((__force_align_arg_pointer__))
#    else
#      define __stdcall __attribute__((__stdcall__))
#      define __cdecl __attribute__((__cdecl__))
#    endif
#  elif defined(__x86_64__)
#    if (__GNUC__ > 5) || ((__GNUC__ == 5) && (__GNUC_MINOR__ >= 3))
#      define __stdcall __attribute__((ms_abi)) __attribute__((__force_align_arg_pointer__))
#      define __cdecl __attribute__((ms_abi)) __attribute__((__force_align_arg_pointer__))
#    else
#      define __stdcall __attribute__((ms_abi))
#      define __cdecl __attribute__((ms_abi))
#    endif
#  elif defined(__arm__)
#    if !defined(__SOFTFP__)
#      define __stdcall __attribute__((pcs("aapcs-vfp")))
#      define __cdecl __attribute__((pcs("aapcs-vfp")))
#    else
#      define __stdcall
#      define __cdecl
#    endif
#  elif defined(__aarch64__)
#     define __stdcall __attribute__((ms_abi))
#     define __cdecl __attribute__((ms_abi))
#  else
#    error no predefined __stdcall for this arch
#  endif
#else
#  define __cdecl
#  error no predefined __stdcall for this compiler
#endif

#define _CDECL      __cdecl
#define CDECL       _CDECL
#define WINAPI      __stdcall

/* export & import */

#ifdef _MSC_VER
# ifdef BUILD_REWINE
#  define REWINE_API __declspec(dllexport)
# else
#  define REWINE_API __declspec(dllimport)
# endif
#elif defined(__GNUC__)
/*
# ifdef BUILD_REWINE
#  define REWINE_API __attribute__((visibility("default")))
# else
#  define REWINE_API
# endif
*/
# define REWINE_API
#else
# error unknown dynamic link import/export semantics.
#endif

/* error codes */

#define DEFINE_HRESULT(bFailure, facility, errcode) \
    ((bFailure ? 0x80000000 : 0x00000000) | 0x20000000 | ((facility) << 4) | (errcode))

// component definitions

#define RWE_COMPONENT_LOADER 0x0001

// error definitions

#define E_RW_LDR_IMAGE_ARCH_MISMATCH                DEFINE_HRESULT(1, RWE_COMPONENT_LOADER, 0x0001)

/* stdlib */

#ifdef __cplusplus
#include <cstddef>
#include <cstdint>
#else
#include <stddef.h>
#include <stdint.h>
#endif

/* API */

typedef void *rw_image_t;

typedef struct rw_elf_t {
    size_t count;
    char *strtab;
    size_t strsz;
    void *symtab;
    void *hash;
    const char *needed;
    const char *soname;
} rw_elf_t;

#ifdef __cplusplus
extern "C" {
#endif

// version

int REWINE_API rewine_echo(void);
const char * REWINE_API rewine_version(void);

// path resolver config

int REWINE_API rewine_AddFindPath(const char *dirpath);
int REWINE_API rewine_RemoveFindPath(const char *dirpath);

// symbol resolver config

int REWINE_API rewine_SetSymbol(
        const char *dll, const char *symbol, int bOverride,
        void * address);
int REWINE_API rewine_SetForwardSymbol(
        const char *dll, const char *symbol, int bOverride,
        const char *forwardToDll, const char *forwardToSymbol);
int REWINE_API rewine_UnsetSymbol(
        const char *dll, const char *symbol, int bOverride);

// open & close

rw_image_t REWINE_API rewine_OpenImage(const char *name);
rw_image_t REWINE_API rewine_OpenImageFd(int fd);
void REWINE_API rewine_CloseImage(rw_image_t handle);

// image getter

void * REWINE_API rewine_GetImageBase(rw_image_t handle);
size_t REWINE_API rewine_GetImageSize(rw_image_t handle);

void * REWINE_API rewine_GetEntryPoint(rw_image_t handle);

// symbol getter

int REWINE_API rewine_GetExportCount(rw_image_t handle);
void * REWINE_API rewine_GetExportByIndex(rw_image_t handle, unsigned int index);
void * REWINE_API rewine_GetExportByName(rw_image_t handle, const char *symbol, unsigned short hint);

void * REWINE_API rewine_ResolveSymbol(const char *dll, const char *symbol, uint16_t hint);

// ELF

rw_elf_t rewine_GetELFInfo(rw_image_t handle, const char *caller);

void REWINE_API rewine_UpdateELFSymtab(rw_image_t handle, uint32_t symcnt, void *symtab, void *strtab);

#ifdef __cplusplus
};
#endif

#endif //REWINE_REWINE_H
